package com.zh.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zh.auth.domain.dto.AddRoleDto;
import com.zh.auth.domain.dto.PageRoleDto;
import com.zh.auth.domain.dto.UpdateRoleDto;
import com.zh.auth.mapper.RoleMapper;
import com.zh.auth.mapper.UserRoleMapper;
import com.zh.auth.service.PermissionService;
import com.zh.auth.service.RolePermissionService;
import com.zh.auth.service.RoleService;
import com.zh.auth.service.UserRoleService;
import com.zh.web.domain.auth.entity.*;
import com.zh.web.domain.auth.vo.RoleVo;
import com.zh.web.enums.ProtectedPermission;
import com.zh.web.enums.ProtectedRole;
import com.zh.web.enums.Status;
import com.zh.common.exception.BusinessException;
import com.zh.web.base.PageApiResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    private final RoleMapper roleMapper;
    private final RolePermissionService rolePermissionService;
    private final PermissionService permissionService;
    private final UserRoleService userRoleService;
    private final UserRoleMapper userRoleMapper;

    @Transactional
    @Override
    public String addRole(AddRoleDto addRoleDto) {
        //本类方法调用需要用本类代理保证事务
        RoleService proxy = (RoleService) AopContext.currentProxy();
        Role role = BeanUtil.copyProperties(addRoleDto, Role.class);
        Integer selectCount = roleMapper.selectCountByCode(role.getRoleCode());
        if (selectCount > 0) {
            throw new BusinessException("角色已存在");
        }
        //没有价值的数据永久删除
        proxy.removeIfMarkedAsDeleted(List.of(role.getRoleCode()));
        return roleMapper.insert(role) > 1 ? "添加成功" : "添加失败";
    }

    @Override
    @Transactional
    public String updateRole(UpdateRoleDto updateRoleDto) {
        RoleService proxy = (RoleService) AopContext.currentProxy();
        Role role = BeanUtil.copyProperties(updateRoleDto, Role.class);
        //是否存在该角色
        Role oldRole = Optional.of(roleMapper.selectById(updateRoleDto.getId()))
                .orElseThrow(() -> new BusinessException("角色不存在"));
        //保护性角色不允许修改,发生修改编码或者名称操作,检测是否修改前后是否为保护性角色
        CompletableFuture<Void> checkProtectedRoleFuture = CompletableFuture.runAsync(() -> {
            if ((ObjUtil.isNotEmpty(updateRoleDto.getRoleCode()) && !updateRoleDto.getRoleCode().equals(oldRole.getRoleCode())) ||
                    (ObjUtil.isNotEmpty(updateRoleDto.getRoleName()) && !updateRoleDto.getRoleName().equals(oldRole.getRoleName()))
            ) {
                List<ProtectedRole> collect = Arrays.stream(ProtectedRole.values())
                        .filter(protectedRole -> (protectedRole.getRoleCode().equals(updateRoleDto.getRoleCode()) ||
                                protectedRole.getRoleName().equals(updateRoleDto.getRoleName())) ||
                                protectedRole.getRoleCode().equals(oldRole.getRoleCode()) ||
                                protectedRole.getRoleName().equals(oldRole.getRoleName())
                        ).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(collect)) {
                    String warning = collect.stream().map(protectedRole ->
                            String.format(("%d:%s"), protectedRole.getRoleCode(), protectedRole.getRoleName())
                    ).collect(Collectors.joining(",", "[", "]"));
                    log.warn("保护性角色,请勿修改,{}", warning);
                    String sb = "保护性角色" + "无法修改" + warning;
                    throw new BusinessException(sb);
                }
            }
        }).whenComplete((result, ex) -> {
            if (ex instanceof BusinessException) {
                throw new BusinessException(ex.getMessage());
            }
        });

        Optional.ofNullable(proxy.getByCode(updateRoleDto.getRoleCode()))
                .ifPresent(role1 -> {
                    throw new BusinessException("角色编码" + role1.getRoleCode() + "重复");
                });
        CompletableFuture.allOf(checkProtectedRoleFuture).join();
        boolean updated = roleMapper.updateById(role) > 0;
        Role updatedRole = Optional.of(roleMapper.selectById(updateRoleDto.getId()))
                .orElseThrow(() -> new BusinessException("更新失败"));
        //同步用户角色关联表编码
        if (ObjUtil.isNotEmpty(updatedRole.getRoleCode())) {
            LambdaUpdateWrapper<UserRole> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(UserRole::getRoleCode, oldRole.getRoleCode());
            updateWrapper.set(ObjUtil.isNotEmpty(updatedRole.getRoleCode()), UserRole::getRoleCode, updatedRole.getRoleCode());
            UserRole userRole = new UserRole();
            userRoleMapper.update(userRole, updateWrapper);
        }
        //全部完成
        return updated ? "更新成功" : "处于最新状态";
    }

    @Override
    public PageApiResult<RoleVo> pageRole(PageRoleDto pageRoleDto) {
        pageRoleDto.setPageNum(pageRoleDto.getPageNum() < 1L ? 1L : pageRoleDto.getPageNum());
        pageRoleDto.setPageSize(pageRoleDto.getPageSize() < 1L ? 10L : pageRoleDto.getPageSize());
        //1. 分页查询角色信息
        Page<RoleVo> page = new Page<>(pageRoleDto.getPageNum(), pageRoleDto.getPageSize());
        CompletableFuture<List<RoleVo>> task1 = CompletableFuture
                .supplyAsync(() -> roleMapper.listRole(page, pageRoleDto))
                .orTimeout(3, TimeUnit.SECONDS)
                .exceptionally(ex -> {
                    log.error(Thread.currentThread().getName() + "异步 RoleVo 异常", ex);
                    if (ex instanceof TimeoutException) {
                        throw new BusinessException("请求超时");
                    }
                    return new ArrayList<>();
                });
        //2. 查询全部角色-权限
        CompletableFuture<List<RolePermission>> task2 = CompletableFuture
                .supplyAsync(rolePermissionService::listRolePermission)
                .orTimeout(3, TimeUnit.SECONDS)
                .exceptionally(ex -> {
                    log.error(Thread.currentThread().getName() + "异步 RolePermission 异常", ex);
                    if (ex instanceof TimeoutException) {
                        throw new BusinessException("请求超时");
                    }
                    return new ArrayList<>();
                });
        //3. 查询全部权限
        CompletableFuture<List<Permission>> task3 = CompletableFuture
                .supplyAsync(permissionService::listPermission)
                .orTimeout(3, TimeUnit.SECONDS)
                .exceptionally(ex -> {
                    log.error(Thread.currentThread().getName() + "异步 Permission 异常", ex);
                    if (ex instanceof TimeoutException) {
                        throw new BusinessException("请求超时");
                    }
                    return new ArrayList<>();
                });
        CompletableFuture.allOf(task1, task2, task3).join();
        List<RoleVo> listRole = task1.getNow(new ArrayList<>());
        List<RolePermission> rolePermissions = task2.getNow(new ArrayList<>());
        List<Permission> allPermission = task3.getNow(new ArrayList<>());
        page.setRecords(listRole);
        listRole.forEach(roleVo -> {
            //4.获取该角色拥有权限编码
            Set<Integer> codeSet = rolePermissions.stream()
                    .filter(rolePermission -> rolePermission.getRoleCode().equals(roleVo.getRoleCode()))
                    .map(RolePermission::getPermissionCode)
                    .collect(Collectors.toSet());
            //5.获取该角色拥有的权限
            List<Permission> permissionList = allPermission.stream()
                    .filter(permission -> codeSet.contains(permission.getPermissionCode()))
                    .collect(Collectors.toList());
            roleVo.setPermissionList(permissionList);
        });
        return PageApiResult
                .<RoleVo>builder()
                .pageNum(page.getCurrent())
                .pageSize(page.getSize())
                .total(page.getTotal())
                .list(page.getRecords())
                .build();
    }

    @Override
    public String deleteRole(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return "id不能为空!";
        }
        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Role::getId, ids);
        List<Role> roleList = Optional.of(roleMapper.selectList(queryWrapper)).orElse(new ArrayList<>());
        //保护性角色不能删除
        Set<Integer> protectedRoleCodeSet = Arrays.stream(ProtectedRole.values()).map(ProtectedRole::getRoleCode).collect(Collectors.toSet());
        List<Role> checkedRole = roleList.stream()
                .filter(role -> protectedRoleCodeSet.contains(role.getRoleCode()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(checkedRole)) {
            String warning = checkedRole.stream().map(protectedRole ->
                    String.format(("%d:%s"), protectedRole.getRoleCode(), protectedRole.getRoleName())
            ).collect(Collectors.joining(",", "[", "]"));
            String sb = "存在保护性角色" + "无法删除" + warning;
            log.warn("保护性角色无法删除,{}", warning);
            throw new BusinessException(sb);
        }
        //判断是否存在已启用的角色
        List<Role> enableRoleList = roleList.stream()
                .filter(role -> Status.ENABLE.equals(role.getStatus()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(enableRoleList)) {
            throw new BusinessException("存在已启用角色，无法删除");
        }

        //判断是否存在用户角色关联
        Set<Integer> codes = roleList.stream().map(Role::getRoleCode).collect(Collectors.toSet());
        List<UserRole> userRoleList = Optional.of(userRoleService.listUserRole()).orElse(new ArrayList<>());
        List<UserRole> usedRoleList = userRoleList.stream()
                .filter(userRole -> codes.contains(userRole.getRoleCode()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(usedRoleList)) {
            StringBuilder sb = new StringBuilder();
            String users = Arrays.toString(usedRoleList.stream().map(UserRole::getUserId).toArray());
            String roles = Arrays.toString(usedRoleList.stream().map(UserRole::getRoleCode).toArray());
            sb.append("删除失败:用户 ")
                    .append(users)
                    .append("关联角色 ")
                    .append(roles);
            throw new BusinessException(sb.toString());
        }
        return roleMapper.deleteBatchIds(ids) > 0 ? "删除角色成功" : "删除角色失败";
    }

    @Override
    public RoleVo getByCode(Integer code) {
        //1. 查询角色信息
        LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Role::getRoleCode, code);
        Role role = roleMapper.selectOne(wrapper);
        if (ObjectUtil.isNull(role)) {
            return null;
        }
        RoleVo roleVo = BeanUtil.copyProperties(role, RoleVo.class);
        //2.获取该角色拥有权限编码
        List<RolePermission> rolePermissionList = rolePermissionService.selectPermissionByRole(role.getRoleCode());
        Set<Integer> codeSet = rolePermissionList.stream().map(RolePermission::getPermissionCode).collect(Collectors.toSet());
        //3.获取全部权限
        List<Permission> allPermission = permissionService.listPermission();
        //3.筛选该角色拥有的权限
        List<Permission> permissionList = allPermission.stream()
                .filter(permission -> codeSet.contains(permission.getPermissionCode()))
                .collect(Collectors.toList());
        roleVo.setPermissionList(permissionList);
        return roleVo;
    }

    /**
     * 根据用户ID查询角色信息
     *
     * @param id 用户ID
     * @return
     */
    public List<RoleVo> getRoleByUserId(Long id) {
        RoleService proxy = (RoleService) AopContext.currentProxy();
        //1.查询用户角色的编码,不存在直接返回
        List<UserRole> userRoleList = Optional.of(userRoleService.selectRoleByUserId(id)).orElse(null);
        if (CollUtil.isEmpty(userRoleList)) {
            return new ArrayList<>();
        }
        //2.查询用户拥有角色编码列表
        List<Integer> roleCodeList = userRoleList.stream()
                .map(UserRole::getRoleCode)
                .collect(Collectors.toList());

        //3.获取每个角色信息
        return roleCodeList.stream()
                .map(proxy::getByCode).collect(Collectors.toList());
    }

    /**
     * 查询角色编码与角色名称的映射
     *
     * @return 角色编码与角色名称的映射
     */
    public Map<String, Integer> getRoleName2Code() {
        Map<String, Integer> roleName2CodeMap = new HashMap<>();
        List<Map<String, Object>> mapList = roleMapper.findRoleName2Code();
        mapList.forEach(map -> {
            String roleName = (String) map.get("role_name");
            Integer roleCode = (Integer) map.get("role_code");
            roleName2CodeMap.put(roleName, roleCode);
        });
        return roleName2CodeMap;
    }

    @Override
    public void removeIfMarkedAsDeleted(List<Integer> codes) {
        roleMapper.permanentlyDeleteByIds(codes);
    }

}
